#include <gtest/gtest.h>
#include <proto_service.hpp>
#include <proto_client.hpp>
#include <numeric>
#include <gmock/gmock-matchers.h>
#include "base.grpc.pb.h"
#include "base_client.hpp"
constexpr int port = 51122;

TEST(proto_async,stream)
{
    asio::io_context io;
    using CType = GRpcServerContext<stream_service::AsyncService>;
    auto& context = asio::use_service<CType>(io);
    auto& svc = asio::use_service<stream_service_service>(io);
    context.listen(port);
    context.bind(&stream_service::AsyncService::Requesttest_param,
        [](meta_data* storage,std::function<asio::awaitable<std::optional<base>>()> stream_param)->asio::awaitable<base>{
        base r;
        r.set_a(0);
        while(auto req = co_await stream_param())
        {
            r.set_a(r.a()+req->a());
        }
        co_return std::move(r);
    });
    context.bind(&stream_service::AsyncService::Requesttest_return,
        [](meta_data* storage,const base& req,std::function<asio::awaitable<void>(std::optional<base>)> sender)->asio::awaitable<void>{
            int k = req.a();
            for(int i=0;i<k;++i){
                base res;
                res.set_a(i);
                co_await sender(res);
            }
            co_return;
    });
    svc.connect(grpc::CreateChannel(std::string{"localhost:"}+std::to_string(port), grpc::InsecureChannelCredentials()));
    int r = 0;
    std::vector<int> sender = {1,2,3,4,5,6};

    int r2 = 5;
    std::vector<int> recive = {};


    asio::co_spawn(io,[&]()->asio::awaitable<void>{
        auto [param,res] = co_await svc.test_param();
        for(auto& i : sender){
            base req;
            req.set_a(i);
            co_await param(req);
        }
        co_await param({});
        try{
            r = (co_await res()).a();    
        }catch(std::exception& e){
            std::cout<<e.what()<<std::endl;
        }catch(grpc::Status& s){
            std::cout<<s.error_message()<<std::endl;
        }

        base req;
        req.set_a(r2);
        auto result = co_await svc.test_return(req);
        while(auto r = co_await result())
        {
            recive.push_back(r->a());
        }


        io.stop();
        co_return;
    },asio::detached);
    io.run();

    ASSERT_EQ(r,std::accumulate(std::begin(sender),std::end(sender),0));
    ASSERT_THAT(recive,testing::ElementsAre(0,1,2,3,4));

}